using Engine;
using Engine.Graphics;
using Engine.Media;
using System;
using System.Text;

namespace Game
{
	public static class PerformanceManager
	{
		public struct FrameData
		{
			public float CpuTime;

			public float TotalTime;
		}

		public static PrimitivesRenderer2D m_primitivesRenderer;

		public static RunningAverage m_averageFrameTime;

		public static RunningAverage m_averageCpuFrameTime;

		public static float? m_longTermAverageFrameTime;

		public static long m_totalMemoryUsed;

		public static long m_totalGpuMemoryUsed;

		public static long m_totalGraphicResourcesCount;

		public static StateMachine m_stateMachine;

		public static double m_totalGameTime;

		public static double m_totalFrameTime;

		public static double m_totalCpuFrameTime;

		public static int m_frameCount;

		public static string m_statsString;

		public static readonly List<string> m_extraStats = [];

		public static FrameData[] m_frameData;

		public static int m_frameDataIndex;

		public static FontBatch2D m_fontBatch;

		public static float? LongTermAverageFrameTime => m_longTermAverageFrameTime;

		public static float AverageFrameTime => m_averageFrameTime.Value;

		public static float AverageCpuFrameTime => m_averageCpuFrameTime.Value;

		public static long TotalMemoryUsed => m_totalMemoryUsed;

		public static long TotalGpuMemoryUsed => m_totalGpuMemoryUsed;

		public static long TotalGraphicResourcesCount => m_totalGraphicResourcesCount;

		static PerformanceManager()
		{
			m_primitivesRenderer = new PrimitivesRenderer2D();
			m_fontBatch = m_primitivesRenderer.FontBatch(BitmapFont.DebugFont, 0, null, null, null, SamplerState.PointClamp);
			m_averageFrameTime = new RunningAverage(1f);
			m_averageCpuFrameTime = new RunningAverage(1f);
			m_stateMachine = new StateMachine();
			m_statsString = string.Empty;
			m_stateMachine.AddState("PreMeasure", delegate
			{
				m_totalGameTime = 0.0;
			}, delegate
			{
				if (GameManager.Project != null)
				{
					m_totalGameTime += Time.FrameDuration;
					if (m_totalGameTime > 60.0)
					{
						m_stateMachine.TransitionTo("Measuring");
					}
				}
			}, null);
			m_stateMachine.AddState("Measuring", delegate
			{
				m_totalFrameTime = 0.0;
				m_totalCpuFrameTime = 0.0;
				m_frameCount = 0;
			}, delegate
			{
				if (GameManager.Project != null)
				{
					if (ScreensManager.CurrentScreen != null && ScreensManager.CurrentScreen.GetType() == typeof(GameScreen))
					{
						float lastFrameTime = Program.LastFrameTime;
						float lastCpuFrameTime = Program.LastCpuFrameTime;
						if (lastFrameTime > 0f && lastFrameTime < 1f && lastCpuFrameTime > 0f && lastCpuFrameTime < 1f)
						{
							m_totalFrameTime += lastFrameTime;
							m_totalCpuFrameTime += lastCpuFrameTime;
							m_frameCount++;
						}
						if (m_totalFrameTime > 180.0)
						{
							m_stateMachine.TransitionTo("PostMeasure");
						}
					}
				}
				else
				{
					m_stateMachine.TransitionTo("PreMeasure");
				}
			}, null);
			m_stateMachine.AddState("PostMeasure", delegate
			{
				if (m_frameCount > 0)
				{
					m_longTermAverageFrameTime = (float)(m_totalFrameTime / m_frameCount);
					float num = (int)Math.Round(Math.Round(m_totalFrameTime / m_frameCount / 0.004999999888241291) * 0.004999999888241291 * 1000.0);
					float num2 = (int)Math.Round(Math.Round(m_totalCpuFrameTime / m_frameCount / 0.004999999888241291) * 0.004999999888241291 * 1000.0);

					Log.Information($"PerformanceManager Measurement: frames={m_frameCount}, avgFrameTime={num}ms, avgFrameCpuTime={num2}ms");
				}
			}, delegate
			{
				if (GameManager.Project == null)
				{
					m_stateMachine.TransitionTo("PreMeasure");
				}
			}, null);
			m_stateMachine.TransitionTo("PreMeasure");
		}

		public static void Update()
		{
			m_averageFrameTime.AddSample(Program.LastFrameTime);
			m_averageCpuFrameTime.AddSample(Program.LastCpuFrameTime);
			if (Time.PeriodicEvent(1.0, 0.0))
			{
				m_totalMemoryUsed = GC.GetTotalMemory(forceFullCollection: false);
				m_totalGpuMemoryUsed = Display.GetGpuMemoryUsage();
				m_totalGraphicResourcesCount = GraphicsResource.m_resources.Count;
			}
			m_stateMachine.Update();
		}

		public static void Draw()
		{
			var scale = new Vector2(MathF.Round(Math.Clamp(ScreensManager.RootWidget.GlobalScale, 1.0f, 2.0f)));
			Viewport viewport = Display.Viewport;
			if (SettingsManager.DisplayFpsCounter)
			{
				if (Time.PeriodicEvent(1.0, 0.0) && ScreensManager.CurrentScreen != null)
				{
					m_statsString = $"CPUMEM {TotalMemoryUsed / 1024f / 1024f:0}MB, GPUMEM {TotalGpuMemoryUsed / 1024f / 1024f:0}MB({TotalGraphicResourcesCount}), CPU {AverageCpuFrameTime / AverageFrameTime * 100f:0}%, {1f / AverageFrameTime:0.0} FPS";
#if DEBUG
					string wname = ScreensManager.RootWidget.Input.MousePosition.HasValue ? ScreensManager.RootWidget.HitTestGlobal(ScreensManager.RootWidget.Input.MousePosition.Value)?.GetType().Name : string.Empty;
					m_statsString += "\nScreen:[" + ScreensManager.CurrentScreen.GetType().Name + "]  [" + wname + "]";
#endif
				}
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine(m_statsString);
				if(m_extraStats.Count > 0)
				{
					stringBuilder.AppendJoin('\n', m_extraStats);
				}
				m_fontBatch.QueueText(stringBuilder.ToString(), Vector2.Transform(Vector2.Zero, ScreensManager.RootWidget.GlobalTransform), 0f, Color.White, TextAnchor.Default, scale, Vector2.Zero);
				m_extraStats.Clear();
			}
			if (SettingsManager.DisplayFpsRibbon)
			{
				float num = (viewport.Width / scale.X > 480f) ? (scale.X * 2f) : scale.X;
				float num2 = viewport.Height / -0.1f;
				float num3 = viewport.Height - 1;
				float s = 0.5f;
				int num4 = MathUtils.Max((int)(viewport.Width / num), 1);
				if (m_frameData == null || m_frameData.Length != num4)
				{
					m_frameData = new FrameData[num4];
					m_frameDataIndex = 0;
				}
				m_frameData[m_frameDataIndex] = new FrameData
				{
					CpuTime = Program.LastCpuFrameTime,
					TotalTime = Program.LastFrameTime
				};
				m_frameDataIndex = (m_frameDataIndex + 1) % m_frameData.Length;
				FlatBatch2D flatBatch2D = m_primitivesRenderer.FlatBatch();
				Color color = Color.Orange * s;
				Color color2 = Color.Red * s;
				for (int num5 = m_frameData.Length - 1; num5 >= 0; num5--)
				{
					int num6 = (num5 - m_frameData.Length + 1 + m_frameDataIndex + m_frameData.Length) % m_frameData.Length;
					FrameData frameData = m_frameData[num6];
					float x = num5 * num;
					float x2 = (num5 + 1) * num;
					flatBatch2D.QueueQuad(new Vector2(x, num3), new Vector2(x2, num3 + (frameData.CpuTime * num2)), 0f, color);
					flatBatch2D.QueueQuad(new Vector2(x, num3 + (frameData.CpuTime * num2)), new Vector2(x2, num3 + (frameData.TotalTime * num2)), 0f, color2);
				}
				flatBatch2D.QueueLine(new Vector2(0f, num3 + (0.0166666675f * num2)), new Vector2(viewport.Width, num3 + (0.0166666675f * num2)), 0f, Color.Green);
			}
			else
			{
				m_frameData = null;
			}
			m_primitivesRenderer.Flush();
		}

		/// <summary>
		/// 在性能信息下方添加一行信息，需每帧添加，不支持中文
		/// </summary>
		public static void AddExtraStat(string stat)
		{
			m_extraStats.Add(stat);
		}
	}
}
